package kr.ac.ssu.ailab.Control;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Vector;

import kr.ac.ssu.ailab.Model.Data.Cell;
import kr.ac.ssu.ailab.Model.Data.Map;

/**
 * Map 에 대한 Control 을 담당하는 클래스
 * 
 * @author Neil
 * 
 */
public class MapControl
{
	public static final int SET_START = 0x001;
	public static final int SET_FINISH = 0x002;
	public static final int SET_OBSTACLES = 0x003;
	public static final int SET_HARD = 0x004;
	public static final int SET_NORMAL = 0x005;
	public static final int SET_THREATEN = 0x006;

	private int level;

	private static boolean isSearch;

	private int count;
	private int mode;
	private int range;
	private int mapNumX;
	private int mapNumY;

	private boolean isCreated;

	private Map map;
	private static MapControl mapControl = null;
	private static Vector<Point> obstacleList = new Vector<Point>();

	/**
	 * 생성자
	 */
	private void MapControl()
	{
	}

	/**
	 * MapControl Instance 를 반환하는 method
	 * 
	 * @return
	 */
	public static MapControl getInstance()
	{
		if (mapControl == null)
			mapControl = new MapControl();

		return mapControl;
	}

	/**
	 * 맵을 생성하는 메소드
	 * 
	 * @param numX
	 * @param numY
	 */
	public void createMap(int numX, int numY)
	{
		setMapNumX(numX);
		setMapNumY(numY);
		map = new Map(numX, numY);
	}

	/**
	 * MapControl Instance 를 null 로 초기화 하는 메소드
	 * 
	 * @return null
	 */
	public static void initInstance()
	{
		if (mapControl != null)
			mapControl = null;
	}

	/**
	 *현재 상태가 Search 상태인지 아닌지를 반환하는 method
	 * 
	 * @return
	 */
	public static boolean getIsSearch()
	{
		return isSearch;
	}

	/**
	 * 현재 Search 상태를 Set 하는 method
	 * 
	 * @param now
	 */
	public static void setIsSearch(boolean now)
	{
		isSearch = now;
	}

	/**
	 * ThreatenRange 를 Set 하는 method
	 * 
	 * @param aRange
	 */
	public void setThreatenRange(int aRange)
	{
		this.range = aRange;
	}

	/**
	 *현재 사용중인 Range 를 get 하는 method
	 * 
	 * @return
	 */
	public int getRange()
	{
		return range;
	}

	/**
	 * StartCell 을 Set 하는 method
	 * 
	 * @param startCell
	 */
	public void setStartCell(Cell startCell)
	{
		map.setStartCell(startCell);
		for (int i = 0; i < getMapNumY(); i++)
			for (int j = 0; j < getMapNumX(); j++)
			{
				getCellArray()[i][j].setDistFromStart(HeuristicPathFinding.calDist(
						getCellArray()[i][j], getStartCell(), 1));
			}

	}

	/**
	 * Finish Cell 을 Set 하는 method
	 * 
	 * @param finishCell
	 */
	public void setFinishCell(Cell finishCell)
	{
		map.setFinishCell(finishCell);
		for (int i = 0; i < getMapNumY(); i++)
			for (int j = 0; j < getMapNumX(); j++)
			{
				getCellArray()[i][j].setDistFromFinish(HeuristicPathFinding.tie_breaking_Heuristic(
						getCellArray()[i][j], getFinishCell(), 1));

			}

	}

	/**
	 * 시작점 Cell 을 반환하는 method
	 * 
	 * @return
	 */
	public Cell getStartCell()
	{
		return Map.getStartCell();
	}

	/**
	 * 목표점 Cell 을 반환하는 method
	 * 
	 * @return
	 */
	public Cell getFinishCell()
	{
		return Map.getFinishCell();
	}

	/**
	 * Cell 을 리턴해주는 method
	 * 
	 * @return
	 */
	public Cell[][] getCellArray()
	{
		return map.getCellArray();
	}

	/**
	 * Cell �� ���� �迭�� �������ִ� method
	 * 
	 * @param clusterStartX
	 * @param clusterStartY
	 * @return
	 */
	public Cell[][] getClusterCellArray(int clusterStartX, int clusterStartY)
	{
		Cell[][] includedCell = null;
		int numIncludedCellX = 0;
		int numIncludedCellY = 0;

		// ( Map의 셀 개수 ) / (클러스터 개수)
		numIncludedCellX = getMapNumX() / ClusterControl.getInstance().getNumX();
		numIncludedCellY = getMapNumY() / ClusterControl.getInstance().getNumY();

		int tempX = clusterStartX * numIncludedCellX;
		int tempY = clusterStartY * numIncludedCellY;

		includedCell = new Cell[numIncludedCellX][numIncludedCellY];

		for (int i = 0; i < numIncludedCellY; i++)
		{
			for (int j = 0; j < numIncludedCellX; j++)
			{
				includedCell[j][i] = getCellArray()[tempX][tempY];
				includedCell[j][i].setIsIncludedIn(count);
				++tempX;
			}
			++tempY;
			tempX = clusterStartX * numIncludedCellX;

		}
		count++;
		return includedCell;

	}

	/**
	 * Map x Size 를 get 하는 method
	 * 
	 * @return
	 */
	public int getMapNumX()
	{
		return mapNumX;
	}

	/**
	 * Map y Size 를 get 하는 method
	 * 
	 * @return
	 */
	public int getMapNumY()
	{
		return mapNumY;
	}

	/**
	 * Map x Size 를 set 하는 method
	 * 
	 * @param x
	 */
	public void setMapNumX(int x)
	{
		mapNumX = x;
	}

	/**
	 * Map Y Size 를 set 하는 method
	 * 
	 * @param y
	 */
	public void setMapNumY(int y)
	{
		mapNumY = y;
	}

	/**
	 * 현재 Cell 주변의 근접한 Cell 을 얻어와서 반환해주는 method
	 * 
	 * @param cell
	 * @return
	 */
	public Cell[] getAdjacentCell(Cell cell)
	{
		int w = map.getNumX();
		int h = map.getNumY();
		ArrayList<Cell> nextList = new ArrayList<Cell>();
		Cell next[] = null;
		Point p = cell.getPosition();
		if (p.y != 0)
		{
			nextList.add(getCellArray()[p.x][p.y - 1]);
		}
		if (p.x != w - 1)
		{
			nextList.add(getCellArray()[p.x + 1][p.y]);
		}
		if (p.y != h - 1)
		{
			nextList.add(getCellArray()[p.x][p.y + 1]);
		}
		if (p.x != 0)
		{
			nextList.add(getCellArray()[p.x - 1][p.y]);
		}

		if ((p.x != 0 && p.y != 0)
				&& ((getCellArray()[p.x][p.y - 1].getType() != CellControl.OBSTACLE) || (getCellArray()[p.x - 1][p.y]
						.getType() != CellControl.OBSTACLE)))
		{
			nextList.add(getCellArray()[p.x - 1][p.y - 1]);
		}
		if ((p.x != w - 1 && p.y != 0)
				&& ((getCellArray()[p.x][p.y - 1].getType() != CellControl.OBSTACLE) || (getCellArray()[p.x + 1][p.y]
						.getType() != CellControl.OBSTACLE)))
		{
			nextList.add(getCellArray()[p.x + 1][p.y - 1]);
		}
		if ((p.x != 0 && p.y != h - 1)
				&& ((getCellArray()[p.x - 1][p.y].getType() != CellControl.OBSTACLE) || (getCellArray()[p.x][p.y + 1]
						.getType() != CellControl.OBSTACLE)))
		{
			nextList.add(getCellArray()[p.x - 1][p.y + 1]);
		}
		if ((p.x != w - 1 && p.y != h - 1)
				&& ((getCellArray()[p.x + 1][p.y].getType() != CellControl.OBSTACLE) || (getCellArray()[p.x][p.y + 1]
						.getType() != CellControl.OBSTACLE)))
		{
			nextList.add(getCellArray()[p.x + 1][p.y + 1]);
		}

		int size = nextList.size();
		next = new Cell[size];

		for (int i = 0; i < size; i++)
		{
			next[i] = nextList.get(i);
		}

		return next;
	}

	/**
	 *현재 위치한 Cell 중에서 근접한 Cell 들중 가장 가중치가 낮은 Cell 을 반환하는 method method
	 * 
	 * @param cell
	 * @return
	 */
	public Cell getLowestAdjacent(Cell cell)
	{

		Cell next[] = getAdjacentCell(cell);

		Cell small = next[0];
		double dist = Double.MAX_VALUE;
		for (int i = 0; i < next.length; i++)
		{

			if (next[i] != null)
			{
				double score = next[i].getDistFromStart();
				// score += next[i].getWeight();
				// score += next[i].getDistFromFinish();

				if (score < dist && score > 0)
				{
					small = next[i];
					dist = score;
				}
			}
		}

		return small;
	}

	/**
	 * 근접한 Cell 을 얻어오는 Method
	 * 
	 * @param cell
	 * @param match
	 * @return
	 */
	public Cell[] getAdjacentLv1Cell(Cell cell, boolean match)
	{
		int w = map.getNumX();
		int h = map.getNumY();
		ArrayList<Cell> nextList = new ArrayList<Cell>();
		Cell next[] = null;
		Point p = cell.getPosition();
		Cell[][] cellList = getCellArray();

		int clusterLv1NumX = getMapNumX() / ClusterControl.getInstance().getNumX();
		int clusterLv1NumY = getMapNumY() / ClusterControl.getInstance().getNumY();

		int nowClusterX = p.x / clusterLv1NumX;
		int nowClusterY = p.y / clusterLv1NumY;

		if (match)
		{
			if (p.y != 0)
			{
				int y = (p.y - 1) / clusterLv1NumY;
				if (nowClusterY == y)
					nextList.add(cellList[p.x][p.y - 1]);
			}
			if (p.x != w - 1)
			{
				int x = (p.x + 1) / clusterLv1NumY;
				if (nowClusterX == x)
					nextList.add(cellList[p.x + 1][p.y]);
			}
			if (p.y != h - 1)
			{
				int y = (p.y + 1) / clusterLv1NumY;
				if (nowClusterY == y)
					nextList.add(cellList[p.x][p.y + 1]);
			}
			if (p.x != 0)
			{
				int x = (p.x - 1) / clusterLv1NumY;
				if (nowClusterX == x)
					nextList.add(cellList[p.x - 1][p.y]);
			}

			if ((p.x != 0 && p.y != 0)
					&& ((cellList[p.x][p.y - 1].getType() != CellControl.OBSTACLE) || (cellList[p.x - 1][p.y]
							.getType() != CellControl.OBSTACLE)))
			{
				int x = (p.x - 1) / clusterLv1NumY;
				int y = (p.y - 1) / clusterLv1NumX;
				if (nowClusterX == x && nowClusterY == y)
					nextList.add(cellList[p.x - 1][p.y - 1]);
			}
			if ((p.x != w - 1 && p.y != 0)
					&& ((cellList[p.x][p.y - 1].getType() != CellControl.OBSTACLE) || (cellList[p.x + 1][p.y]
							.getType() != CellControl.OBSTACLE)))
			{
				int x = (p.x + 1) / clusterLv1NumY;
				int y = (p.y - 1) / clusterLv1NumX;
				if (nowClusterX == x && nowClusterY == y)
					nextList.add(cellList[p.x + 1][p.y - 1]);
			}
			if ((p.x != 0 && p.y != h - 1)
					&& ((cellList[p.x - 1][p.y].getType() != CellControl.OBSTACLE) || (cellList[p.x][p.y + 1]
							.getType() != CellControl.OBSTACLE)))
			{
				int x = (p.x - 1) / clusterLv1NumY;
				int y = (p.y + 1) / clusterLv1NumX;
				if (nowClusterX == x && nowClusterY == y)
					nextList.add(cellList[p.x - 1][p.y + 1]);
			}
			if ((p.x != w - 1 && p.y != h - 1)
					&& ((cellList[p.x + 1][p.y].getType() != CellControl.OBSTACLE) || (cellList[p.x][p.y + 1]
							.getType() != CellControl.OBSTACLE)))
			{
				int x = (p.x + 1) / clusterLv1NumY;
				int y = (p.y + 1) / clusterLv1NumX;
				if (nowClusterX == x && nowClusterY == y)
					nextList.add(cellList[p.x + 1][p.y + 1]);
			}
		}
		else
		{
			if (p.y != 0)
			{
				nextList.add(cellList[p.x][p.y - 1]);
			}
			if (p.x != w - 1)
			{
				nextList.add(cellList[p.x + 1][p.y]);
			}
			if (p.y != h - 1)
			{
				nextList.add(cellList[p.x][p.y + 1]);
			}
			if (p.x != 0)
			{
				nextList.add(cellList[p.x - 1][p.y]);
			}
			// ���⼭ ���� �밢
			if ((p.x != 0 && p.y != 0)
					&& ((cellList[p.x][p.y - 1].getType() != CellControl.OBSTACLE) || (cellList[p.x - 1][p.y]
							.getType() != CellControl.OBSTACLE)))
			{
				nextList.add(cellList[p.x - 1][p.y - 1]);
			}
			if ((p.x != w - 1 && p.y != 0)
					&& ((cellList[p.x][p.y - 1].getType() != CellControl.OBSTACLE) || (cellList[p.x + 1][p.y]
							.getType() != CellControl.OBSTACLE)))
			{
				nextList.add(cellList[p.x + 1][p.y - 1]);
			}
			if ((p.x != 0 && p.y != h - 1)
					&& ((cellList[p.x - 1][p.y].getType() != CellControl.OBSTACLE) || (cellList[p.x][p.y + 1]
							.getType() != CellControl.OBSTACLE)))
			{
				nextList.add(cellList[p.x - 1][p.y + 1]);
			}
			if ((p.x != w - 1 && p.y != h - 1)
					&& ((cellList[p.x + 1][p.y].getType() != CellControl.OBSTACLE) || (cellList[p.x][p.y + 1]
							.getType() != CellControl.OBSTACLE)))
			{
				nextList.add(cellList[p.x + 1][p.y + 1]);
			}
		}
		int size = nextList.size();
		next = new Cell[size];

		for (int i = 0; i < size; i++)
		{
			next[i] = nextList.get(i);
		}

		return next;
	}

	/**
	 * Map의 각각의 Cell 을 Clean 하는 Method
	 */
	public void clearMap()
	{
		for (int i = 0; i < map.getNumX(); i++)
		{
			for (int j = 0; j < map.getNumY(); j++)
			{
				getCellArray()[i][j] = null;

			}
		}
	}

	/**
	 * Map에서 Obstacle을 제외하고 나머지를 Clear 시키는 Method
	 */
	public void clearMapExceptObs()
	{
		for (int i = 0; i < map.getNumX(); i++)
		{
			for (int j = 0; j < map.getNumY(); j++)
			{
				if (getCellArray()[i][j].getType() != CellControl.OBSTACLE
						&& getCellArray()[i][j].getType() != CellControl.HARD)
				{
					// CellControl.setCellType(getCellArray()[i][j],
					// CellControl.NORMAL,
					// CellControl.NORMAL_COST);
					CellControl.setNormalCell(getCellArray()[i][j]);
					// PathFinder.demoArea.remove
					getCellArray()[i][j].setDistFromFinish(0);
					getCellArray()[i][j].setDistFromStart(0);
				}

			}
		}

	}

	/**
	 * Map 을 Set 하는 method
	 * 
	 * @param load
	 */
	public void setMap(Map load)
	{
		map = load;
	}

	/**
	 * Map 을 get 하는 method
	 * 
	 * @return
	 */
	public Map getMap()
	{
		return map;
	}

	/**
	 * Count 를 set 하는 method
	 * 
	 * @return
	 */
	public int getCount()
	{
		return count;
	}

	/**
	 * Count 를 get 하는 method
	 * 
	 * @param count
	 */
	public void setCount(int count)
	{
		this.count = count;
	}

	/**
	 * 현재 난이도를 Get 하는 Method
	 * 
	 * @return
	 */
	public int getLevel()
	{
		return level;
	}

	/**
	 * 현재 난이도를 set 하는 method
	 * 
	 * @param level
	 */
	public void setLevel(int level)
	{
		this.level = level;
	}

	/**
	 * Obstacle List 를 get 하는 메소드
	 * 
	 * @return
	 */
	public static Vector<Point> getObstacleList()
	{
		return obstacleList;
	}

	/**
	 * Obstacle List 를 add 하는 메소드
	 * 
	 * @param obstacleList
	 */
	public static void addObstacleList(Point point)
	{
	
		obstacleList.add(point);
	}

	/**
	 * Obstacle List 중 하나를 remove 하는 메소드
	 * 
	 * @param point
	 */
	public static void removeObstacleList(Point point)
	{
		if (obstacleList.contains(point))
			obstacleList.remove(point);
	}

}
